package com.skivingcloud.admin.sys.controller;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.skivingcloud.admin.shiro.PasswordExpired;
import com.skivingcloud.admin.sys.entity.User;
import com.skivingcloud.admin.sys.model.SysLoginForm;
import com.skivingcloud.admin.sys.service.ISysCaptchaService;
import com.skivingcloud.admin.sys.service.ITenantService;
import com.skivingcloud.admin.sys.service.IUserService;
import com.skivingcloud.admin.utils.PasswordHelper;
import com.skivingcloud.admin.utils.UserUtil;
import com.skivingcloud.common.contants.CommonServiceError;
import com.skivingcloud.common.contants.Constant;
import com.skivingcloud.common.utils.R;
import com.skivingcloud.common.utils.StringUtils;
import com.skivingcloud.admin.annotation.ModifyAnnotaionFactory;
import com.skivingcloud.admin.annotation.UserOperateLog;
import com.skivingcloud.admin.contants.CustomToken;
import com.skivingcloud.admin.contants.UrlConstant;


import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 登录相关
 * 
 * @author hushouquan
 * @date 2018-11-24
 */
@RestController
@RequestMapping(UrlConstant.WEB_ADMIN_URL+"/login")
public class SysLoginController {
	
	private static Logger log = LoggerFactory.getLogger(SysLoginController.class);
	
//	@Autowired
//	private SysUserService sysUserService;
	@Autowired
	private ISysCaptchaService sysCaptchaService;

	@Autowired
	private PasswordExpired passwordExpired;
	
	@Autowired
	private IUserService userService;
	@Autowired
	private ITenantService tenantService;

	/**
	 * 获取验证码
	 * @throws Exception 
	 */
	@GetMapping("/captcha.jpg")
	public void captcha(HttpServletResponse response, String uuid)throws Exception {
		response.setHeader("Cache-Control", "no-store, no-cache");
		response.setContentType("image/jpeg");

		//获取图片验证码
		BufferedImage image = sysCaptchaService.getCaptcha(uuid);

		ServletOutputStream out = response.getOutputStream();
		ImageIO.write(image, "png", out);
		IOUtils.closeQuietly(out);
	}

	/**
	 * 登录
	 */
	@PostMapping("/login")
	@UserOperateLog(module="系统功能",methods="login",description="系统登录")
	public Map<String, Object> login(@RequestBody SysLoginForm form, HttpServletRequest request, HttpServletResponse response)throws IOException {
		boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());
		if(!captcha){
			return R.error("验证码不正确");
		}
		if(StringUtils.isBlank(form.getTerminal())){
			return R.error("请传入终端类型");
		}
		String tenantCode = form.getTenantId();
		if(StringUtils.isBlank(tenantCode)) {
			return R.error("租户编号不正确");
		}
		String tenantId = tenantService.getTenantIdByCode(tenantCode);
		if(StringUtils.isBlank(tenantId)) {
			return R.error("租户编号不正确");
		}
		form.setTenantId(tenantId);
		//用户信息
//		User user = sysUserService.queryByUserName(form.getUsername());
		// 想要得到 SecurityUtils.getSubject()　的对象．．访问地址必须跟shiro的拦截地址内．不然后会报空指针
		Subject subject = SecurityUtils.getSubject();
		// 用户输入的账号和密码,,存到UsernamePasswordToken对象中..然后由shiro内部认证对比,
		// 认证执行者交由ShiroDbRealm中doGetAuthenticationInfo处理
		// 当以上认证成功后会向下执行,认证失败会抛出异常
//		UsernamePasswordToken token = new UsernamePasswordToken(form.getUsername(), form.getPassword());
		CustomToken token = new CustomToken(form.getUsername(), form.getPassword(), form.getTenantId());
		try {
			token.setTerminal(form.getTerminal());
			//通过Shiro进行用户名密码验证
			subject.login(token);
			
			//将用户登陆信息保存到数据库
			Session session = SecurityUtils.getSubject().getSession();
//			UserLoginLog userLoginLog = new UserLoginLog();
//			userLoginLog.setUserId((String)session.getAttribute("userSessionId"));
//			userLoginLog.setAccountName(loginName);
//			userLoginLog.setLoginIp(session.getHost());
//			userLoginLogService.addUserLoginLog(userLoginLog);
			//登陆成功
			//R r =new R();
			//更新最后登录时间
			User user = (User) session.getAttribute(Constant.USERSESSION);
			ModifyAnnotaionFactory.newInstance().ModifyAnnotation(getClass(), UserOperateLog.class, "login", "bussid", user.getId(), form.getClass(), HttpServletRequest.class, HttpServletResponse.class);
//			System.out.println("---------调用方法中打印：" + annoUserOperateLog.bussid());
			//上次登录时间
			User userUpdate = new User();
//			BeanUtils.copyProperties(user, userUpdate);
			userUpdate.setId(user.getId());
			userUpdate.setLoginTime(LocalDateTime.now());
			userUpdate.setLoginTerminal(form.getTerminal());
			userService.update(userUpdate);
//			SimpleCookie simpleCookie = new SimpleCookie("signtoken");//设置一个token给电子签章使用
//			simpleCookie.setValue(String.valueOf(subject.getSession().getId()));
//			simpleCookie.setHttpOnly(false);
//			simpleCookie.saveTo(request, response);
//			SimpleCookie mofCookie = new SimpleCookie("ANNUITY-JSESSIONID");//设置一个token给分类资产研究工具使用
//			mofCookie.setValue(String.valueOf(subject.getSession().getId()));
//			mofCookie.setHttpOnly(false);
//			mofCookie.saveTo(request, response);
//			Date lcpt = user.getLastChangedPwdTime().;
			Duration duration = Duration.between(user.getLastChangedPwdTime(), LocalDateTime.now());
//			int days = DateUtils.compareToDays(new Date(), lcpt);
			long days = duration.toDays();
			if(passwordExpired.getDays() - days < passwordExpired.getAlarm()) {
				if(passwordExpired.isForcechangepwd() && passwordExpired.getDays() - days < passwordExpired.getForcedays()) {
					return R.error(976, "您的密码即将过期，请您修改密码！").put("token", user.getId()).put("user", user);
				}
				return R.error(977, "您的密码即将过期，请您尽快修改密码。如到期未修改账户将被停用！").put("token", user.getId()).put("user", user);
			}
			return R.ok().put("token", user.getId()).put("user", user);//.put("token", token);
		} catch(UnknownAccountException uae){
			token.clear();
			return R.error("用户未找到");//用户未找到
		} catch(IncorrectCredentialsException ice){
			token.clear();
			return R.error("密码错误");//密码错误
		}catch (LockedAccountException lae) {
			token.clear();
			return R.error("用户已经被锁定");//用户已经被锁定,不能登录
		} catch (ExcessiveAttemptsException e) {
			token.clear();
			return R.error("登录失败次数过多");//登录失败次数过多
		} catch (DisabledAccountException e) {
			token.clear();
			return R.error("账户已被禁用");//账户已被禁用
		} catch (Exception e){
			token.clear();
			e.printStackTrace();
			return R.error("登录失败");//登录失败
		}
	}

	/**
	 * 统一身份认证登录接口 
	 * @param iamInfo {"iamCode":身份认证返回的code, "terminal":终端类型({@link Constant.TerminalType}
	 * @return
	 * @throws IOException
	 *//*
	@PostMapping("/iamLogin")
	@UserOperateLog(module="系统功能",methods="iamLogin",description="通过统一身份认证登录系统",functionNo="101055")
	public R iamLogin(@RequestBody Map<String, String> iamInfo, HttpServletRequest request, HttpServletResponse response)throws Exception {
		CustomToken token = new CustomToken();
		try {
			//第一步，根据授权码iamCode请求token
//			Map<String, String> tokenUriVariables = new LinkedHashMap<String, String>();
//			tokenUriVariables.put("appcode", commonPropertiesConfig.getIamAppcode());
//			tokenUriVariables.put("secret", commonPropertiesConfig.getIamSecret());
//			tokenUriVariables.put("code", iamInfo.get("iamCode"));
			if(!iamInfo.containsKey("terminal")) {
				return R.error("请传入终端类型");
			}
			String appcode = "";
			String secret = "";
			if(Constant.TerminalType.TRUSTEE_PC.getValue().equals(iamInfo.get("terminal"))) {
				appcode = iamPropertiesConfig.getTrusteeIamAppcode();
				secret = iamPropertiesConfig.getTrusteeIamSecret();
			}else if(Constant.TerminalType.ENTRUST_PC.getValue().equals(iamInfo.get("terminal"))) {
				appcode = iamPropertiesConfig.getEntrustIamAppcode();
				secret = iamPropertiesConfig.getEntrustIamSecret();
			}else {
				return R.error("请传入正确的终端类型");
			}
			
			ResponseEntity<JSONObject> tokenResponse = restTemplateOuter.getForEntity(iamPropertiesConfig.getIamUrl() + UrlConstant.IAM_TOKEN_URL + "?appcode=" + appcode + "&secret=" + secret + "&code=" + iamInfo.get("iamCode"), JSONObject.class);
			JSONObject tokenObj = tokenResponse.getBody();
			if(tokenObj.containsKey("errorCode")) {
				log.error("调用身份认证获取token信息失败：" + tokenObj.get("errorMsg") == null ? "" : tokenObj.getString("errorMsg"));
				return R.error("调用身份认证获取token信息失败：" + tokenObj.get("errorMsg") == null ? "" : tokenObj.getString("errorMsg"));
			}
			if(!tokenObj.containsKey("accessToken")) {
				log.error("调用身份认证获取token信息失败：" + tokenObj.get("errorMsg") == null ? "" : tokenObj.getString("errorMsg"));
				return R.error("调用身份认证获取token信息失败：" + tokenObj.get("errorMsg") == null ? "" : tokenObj.getString("errorMsg"));
			}
			//第二步，根据token获取用户信息
//			Map<String, String> userinfoUriVariables = new LinkedHashMap<String, String>();
//			userinfoUriVariables.put("appcode", commonPropertiesConfig.getIamAppcode());
//			userinfoUriVariables.put("secret", commonPropertiesConfig.getIamSecret());
//			userinfoUriVariables.put("token", tokenObj.getString("accessToken"));
			ResponseEntity<JSONObject> userinfoResponse = restTemplateOuter.getForEntity(iamPropertiesConfig.getIamUrl() + UrlConstant.IAM_USERINFO_URL  + "?appcode=" + appcode + "&secret=" + secret + "&token=" + tokenObj.getString("accessToken"), JSONObject.class);
			JSONObject userinfoObj = userinfoResponse.getBody();
			if(!userinfoObj.containsKey("accountName")) {
				log.error("调用身份认证获取用户信息失败：" + userinfoObj.get("errorMsg") == null ? "" : userinfoObj.getString("errorMsg"));
				return R.error("调用身份认证获取用户信息失败：" + userinfoObj.get("errorMsg") == null ? "" : userinfoObj.getString("errorMsg"));
			}
			User user = new User();
			user.setAccountName(userinfoObj.getString("accountName"));
			List<User> userList = sysUserAuthService.getUser(user);
			if(userList.size() == 0) {
				return R.error("年金系统中不存在该用户，请先联系管理员添加用户！");
			}
			User u = userList.get(0);
			if(Constant.USER_STATUS_DELETED.equals(u.getStatus())) {
				return R.error("年金系统中不存在该用户，请先联系管理员添加用户！");
			}
			if(Constant.USER_STATUS_LOCKED.equals(u.getStatus())) {
				return R.error("用户已被锁定，请联系管理员解锁！");
			}
			if(Constant.USER_STATUS_DISABLED.equals(u.getStatus())) {
				return R.error("用户已被禁用，请联系管理员启用！");
			}
			//第三步，终于可以登录了,在这儿免密登录创建会话
			token = new CustomToken(u.getUsername());
			token.setTerminal(iamInfo.get("terminal"));
			//通过Shiro登录
			Subject subject = SecurityUtils.getSubject();
			subject.login(token);
			//这个是记操作日志的
			ModifyAnnotaionFactory.newInstance().ModifyAnnotation(getClass(), UserOperateLog.class, "iamLogin", "bussid", u.getId(), iamInfo.getClass());
			//上次登录时间
			User userUpdate = new User();
			userUpdate.setId(user.getId());
			userUpdate.setLoginTime(new Date());
			userUpdate.setLoginTerminal(iamInfo.get("terminal"));
			sysUserService.update(userUpdate);
			SimpleCookie simpleCookie = new SimpleCookie("signtoken");//设置一个token给电子签章使用
			simpleCookie.setValue(String.valueOf(subject.getSession().getId()));
			simpleCookie.setHttpOnly(false);
			simpleCookie.saveTo(request, response);
			SimpleCookie mofCookie = new SimpleCookie("ANNUITY-JSESSIONID");//设置一个token给分类资产研究工具使用
			mofCookie.setValue(String.valueOf(subject.getSession().getId()));
			mofCookie.setHttpOnly(false);
			mofCookie.saveTo(request, response);
			return R.ok();
		} catch(UnknownAccountException uae){
			if(token != null){
				token.clear();
			}
			return R.error("用户未找到");//用户未找到
		} catch(IncorrectCredentialsException ice){
			if(token != null){
				token.clear();
			}
			return R.error("密码错误");//密码错误
		}catch (LockedAccountException lae) {
			if(token != null){
				token.clear();
			}
			return R.error("用户已经被锁定");//用户已经被锁定,不能登录
		} catch (ExcessiveAttemptsException e) {
			if(token != null){
				token.clear();
			}
			return R.error("登录失败次数过多");//登录失败次数过多
		} catch (DisabledAccountException e) {
			if(token != null){
				token.clear();
			}
			return R.error("账户已被禁用");//账户已被禁用
		}catch (Exception e){
			if(token != null){
				token.clear();
			}
			log.error("通过统一身份认证登录失败", e);
			return R.error("登录失败");//登录失败
		}
	}*/
	/**
	 * 退出
	 */
	@UserOperateLog(module="系统功能",methods="logout",description="退出登录")
	@PostMapping("/logout")
	public Map<String, Object> logout() {
		//sysUserTokenService.logout(getUserId());
		User user = UserUtil.getUser();
		ModifyAnnotaionFactory.newInstance().ModifyAnnotation(getClass(), UserOperateLog.class, "logout", "bussid", user.getId());
		Subject subject = SecurityUtils.getSubject();
		subject.logout();//shiro退出
//		ResponseEntity<JSONObject> userinfoResponse = restTemplateOuter.getForEntity(commonPropertiesConfig.getIamUrl() + UrlConstant.IAM_USERINFO_URL  + "?appcode=" + commonPropertiesConfig.getIamAppcode() + "&secret=" + commonPropertiesConfig.getIamSecret() + "&token=" + tokenObj.getString("accessToken"), JSONObject.class);
//		R r =new R();
		return R.ok();
	}
	
	@GetMapping("/unauth")
	public R unauth() {
		return R.error(HttpStatus.SC_UNAUTHORIZED, "没有权限，请登录！");
	}

	/**
	 *	解锁密码验证
	 * @param form 密码参数
	 * @param request
	 * @param response
	 * @return
	 */
	@PostMapping("/unblock")
	public R unblock(@RequestBody SysLoginForm form, HttpServletRequest request, HttpServletResponse response){
		try {
			User user = UserUtil.getUser();
			//老密码
			String oldPassword = user.getPassword();
			user.setPassword(form.getPassword());
			user = PasswordHelper.encryptPassword(user);
			if(user.getPassword().equals(oldPassword)){
				return R.ok();
			}else {
				return R.error("密码不正确！");
			}
		} catch (Exception e) {
			log.error(CommonServiceError.SERVICE_SYSTEM_EXCEPTION.getMsg(), e);
			return R.error(CommonServiceError.SERVICE_SYSTEM_EXCEPTION.getMsg());
		}
	}
	
	/**
	 * 随便调用个方法续订下session
	 * @return
	 */
	@GetMapping("/restSessionTime")
	public R restSessionTime(){
		return R.ok().put("res","续订会话成功");
	}
}
